package org.myspringframework.core;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ClassName:ClassPathXmlApplicationContext
 * Package:org.myspringframework.core
 * Description:
 *
 * @Author:ZhangXin
 * @Create:2023/5/9-16:02
 * @Version:v1.0
 */
public class ClassPathXmlApplicationContext implements ApplicationContext {

    private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);

    private final Map<String, Object> singletonObjects = new HashMap<>();

    //解析myspirng的配置文件,然后初始化所有Bean对象
    //configLocation spring配置文件的路径
    public ClassPathXmlApplicationContext(String configLocation) {

        try {
            //解析spring-myspring.xml文件,然后实例化Bean,将Bean存放到singletonObject集合当中
            //dom4j解析XML文件的核心对象
            SAXReader saxReader = new SAXReader();
            //获取输入流,指向配置文件
            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            //读文件
            Document document = saxReader.read(in);
            //获取所有的 bean 标签
            List<Node> nodes = document.selectNodes("//bean");
            //遍历bean标签
            nodes.forEach(node -> {
                try {
                    //System.out.println(node);
                    //向下转型的目的是为了使用Element接口里更加丰富的方法
                    Element beanElt = (Element) node;
                    //获取id属性
                    String id = beanElt.attributeValue("id");
                    //获取class属性
                    String className = beanElt.attributeValue("class");
                    logger.info("beanName=" + id);
                    logger.info("beanClassName=" + className);
                    //通过反射机制创建对象,将其放到Map集合中,提前曝光
                    //获取class
                    Class<?> aClass = Class.forName(className);
                    //调用无参数构造方法
                    Constructor<?> constructor = aClass.getDeclaredConstructor();
                    //调用无参数构造方法实例化bean
                    Object bean = constructor.newInstance();
                    //将Bean曝光,加入Map集合
                    singletonObjects.put(id, bean);
                    //记录日志
                    logger.info(singletonObjects.toString());

                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            //再次重新遍历bean标签,这一次是给对象属性赋值
            nodes.forEach(node -> {
                try {
                    Element beanElt = (Element) node;
                    //获取id
                    String id = beanElt.attributeValue("id");
                    //获取className
                    String className = beanElt.attributeValue("class");
                    //获取class
                    Class<?> aClass = Class.forName(className);
                    //获取该bean标签下所有的property标签
                    List<Element> properties = beanElt.elements("property");
                    //遍历所有的属性标签
                    properties.forEach(property -> {

                        try {
                            //获取属性名
                            String propertyName = property.attributeValue("name");
                            //获取属性类型
                            Field field = aClass.getDeclaredField(propertyName);

                            logger.info("属性名:" + propertyName);
                            //获取set方法名
                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                            //获取set
                            Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());
                            //获取具体的值
                            String value = property.attributeValue("value");

                            String ref = property.attributeValue("ref");
                            Object actualValue = null;
                            if (value != null) {
                                //说明这个值是简单类型
                                //调用set方法(set方法没有返回值)
                                //声明允许的类型
                                //获取属性类型名
                                String propertyTypeSimpleName = field.getType().getSimpleName();
                                switch (propertyTypeSimpleName) {
                                    case "byte" -> actualValue = Byte.parseByte(value);
                                    case "short" -> actualValue = Short.parseShort(value);
                                    case "int" -> actualValue = Integer.parseInt(value);
                                    case "long" -> actualValue = Long.parseLong(value);
                                    case "float" -> actualValue = Float.parseFloat(value);
                                    case "double" -> actualValue = Double.parseDouble(value);
                                    case "boolean" -> actualValue = Boolean.parseBoolean(value);
                                    case "char" -> actualValue = value.charAt(0);
                                    case "Byte" -> actualValue = Byte.valueOf(value);
                                    case "Short" -> actualValue = Short.valueOf(value);
                                    case "Integer" -> actualValue = Integer.valueOf(value);
                                    case "Long" -> actualValue = Long.valueOf(value);
                                    case "Float" -> actualValue = Float.valueOf(value);
                                    case "Double" -> actualValue = Double.valueOf(value);
                                    case "Boolean" -> actualValue = Boolean.valueOf(value);
                                    case "Character" -> actualValue = Character.valueOf(value.charAt(0));
                                    case "String" -> actualValue = value;
                                }
                                setMethod.invoke(singletonObjects.get(id),actualValue);
                            }
                            if (ref != null) {
                                //说明这个值是非简单类型
                                //调用set方法(set方法没有返回值)
                                setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    @Override
    public Object getBean(String beanName) {
        return singletonObjects.get(beanName);
    }
}
